#include "uthread_struct.h"
#include "lista.h"
#include <stdlib.h>
#include <stdio.h>

/* Fila de Threads Aptas */
static listaThreads *filaAptos = NULL;

/* Fila de Threads Bloqueadas */
static listaThreads *listaBloqueadas = NULL;

/* Fila de todas as threads criadas */
static listaThreads *filaTodasThreads;

/* VariÃ¡vel para alocar a thread em execuÃ§Ã£o */
static tcb *threadAtual;



/**
* FunÃ§Ã£o que inicializa as variÃ¡veis para a biblioteca uthread
* @return int 0 se obteve sucesso, senÃ£o retorna -1
*/
int uth_init(void){
    /* Thread "Main" é a thread atual */
    tcb *tcb1;
    tcb1 = alocarThread();
    tcb1->estado = EXECUTANDO;
    getcontext(&tcb1->contexto);
    threadAtual = tcb1;
    filaTodasThreads = inserir(filaTodasThreads, tcb1);
    if (threadAtual != NULL)
        return 0;
    else
        return -1;
 }

/**
*  FunÃ§Ã£o que cria uma thread, colocando-a na fila de aptos
*  @return int (tid da thread criada).
*/
int uth_create(void * (*funcaoInicial)(void*), void * arg){

	/*InicializaÃ§Ã£o*/
	tcb *tcb1;
	tcb1 = alocarThread();
	tcb1->funcao = funcaoInicial;
	tcb1->arg = arg;
	getcontext(&tcb1->contexto); //salva o contexto atual

    /*Insere Thread criada na fila de aptos*/
    filaAptos = inserir(filaAptos, tcb1);

    /*Insere Thread criada na fila de todas as threads*/
    filaTodasThreads = inserir(filaTodasThreads, tcb1);

	return tcb1->id;
}

/**
*  FunÃ§Ã£o que retira a thread que estÃ¡ executando e a envia para a fila de aptos
*  Pega o primeira thread da fila de aptos e executa
*/
int uth_yield(void){
    tcb *threadParaExecutar, *tcb1;

    /* Reinserir thread atual na lista de aptos */
    threadAtual->estado = APTO_JA_EXECUTADO;
    getcontext(&threadAtual->contexto);
    filaAptos = inserir(filaAptos, threadAtual);

    //imprimeListaThreads(filaAptos);

    /* Executa a primeira thread da fila de aptos */
    threadParaExecutar = buscarPrimeiroElemento(filaAptos); //Busca 1o elemento da fila
    if (threadParaExecutar == NULL){ //se sim, nÃ£o havia threads na fila de aptos
        //o que fazer aqui??  Este caso nunca acontece jÃ¡ que quando chamamos a funÃ§Ã£o YELD
        //retiramos uma thread em execuÃ§Ã£o e a colocamos na fila de APTO
        printf ("Nao havia threads na fila de aptos");
        return -1;
    } else {

        threadAtual = threadParaExecutar;
        filaAptos = eliminaPrimeiroElemento(filaAptos);  //atualiza a fila de aptos tirando a thread que foi para o estado executando
        if(threadAtual->estado == APTO_NUNCA_EXECUTADO){
            tcb *threadAux;
            threadAux = threadAtual;
            threadAtual->estado = EXECUTANDO;
            threadAtual->funcao(*((int*)threadAtual->arg)); //th1foo2 parou aqui
            threadAtual = threadAux;
            //setcontext(&threadAtual->contexto);
        }
        else{
            threadAtual->estado = EXECUTANDO;
            setcontext(&threadAtual->contexto);
        }

     filaAptos = removeElementoPorId(filaAptos, threadAtual->id);
     threadAtual->estado = TERMINADO;

     return 0;

    }
}


int uth_wait(int thr){
    /* Bloqueia a thread em execucao */
    if(buscaIdElemento())
    {
        listaBloqueadas = listaBloqueiaThread(listaBloqueadas,threadAtual);
        tcb *tcbAux, *threadParaExecutar;
        tcbAux = threadAtual;
    }

     /* Executa a primeira thread da fila de aptos */
    threadParaExecutar = buscarPrimeiroElemento(filaAptos); //Busca 1o elemento da fila
    if (threadParaExecutar == NULL){ //se sim, nÃ£o havia threads na fila de aptos
        //o que fazer aqui??  Este caso nunca acontece jÃ¡ que quando chamamos a funÃ§Ã£o YELD
        //retiramos uma thread em execuÃ§Ã£o e a colocamos na fila de APTO
        printf ("Nao havia threads na fila de aptos");
        return -1;
    } else {

        threadAtual = threadParaExecutar;
        filaAptos = eliminaPrimeiroElemento(filaAptos);  //atualiza a fila de aptos tirando a thread que foi para o estado executando
        if(threadAtual->estado == APTO_NUNCA_EXECUTADO){
            tcb *threadAux;
            threadAux = threadAtual;
            threadAtual->estado = EXECUTANDO;
            threadAtual->funcao(*((int*)threadAtual->arg)); //th1foo2 parou aqui
            threadAtual = threadAux;
            //setcontext(&threadAtual->contexto);
        }
        else{
            threadAtual->estado = EXECUTANDO;
            setcontext(&threadAtual->contexto);
        }

    if(threadAtual->id = thr)
    {
        tcbAux->estado = APTO_JA_EXECUTADO;
        filaAptos = inserir(filaAptos, tcbAux);
        listaBloqueadas = removeElementoPorId(listaBloqueadas ,tcbAux->id);
    }
    filaAptos = removeElementoPorId(filaAptos, threadAtual->id);
    threadAtual->estado = TERMINADO;

    //imprimeListaThreads(filaAptos);

    return 0;

    }
} //IMPLEMENTAR ESSA FUNÃ‡ÃƒO

